#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define SEM_H 0 //Para referenciar al semaforo que controla el acceso de los hijos

//Macros para simplificar la operación sobre un semáforo
#define SEM_WAIT(X, SEMID, OP, FLAG) { \
	OP.sem_num = X; \
	OP.sem_op = -1; \
	OP.sem_flg = FLAG; \
	semop(SEMID, &OP, 1); }

#define SEM_POST(X, SEMID, OP, FLAG) { \
	OP.sem_num = X; \
	OP.sem_op = 1; \
	OP.sem_flg = FLAG; \
	semop(SEMID, &OP, 1); }

int main()
{
	int dinerobanco=0; //Variable para almacenar el dinero total del banco
	const int ncuentas=10; //Constante para el numero de cuentas utilizadas
	const int hijos=20; //Constante para el numero de hijos que relizan transferencias
	key_t llave; //Llave para la generación de la memoria compartida y el semaforo
        int shmid; //Id de la zona compartida
        int * bufescritura; //Puntero de acceso a la zona compartida
        int i;
	pid_t hijo=0;
	struct sembuf oper;
	int semid;
        llave=ftok("/bin/ls",'c');
        if (llave<0)
        {
                perror("Error al crear la llave");
                exit(EXIT_FAILURE);
        }
        else
        {
                shmid=shmget(llave,ncuentas*sizeof(int),IPC_CREAT | 0600);
                printf("El valor de la llave es %d \n",llave);
                printf("El valor de id de memoria compartida es %d\n",shmid);
                if (shmid<0)
                {
                        perror("Error al obtener el id de la zona de memoria compartida");
                        exit(EXIT_FAILURE);
                }
                else
                {
                        bufescritura=(int *) shmat(shmid,NULL,0);
                        if (bufescritura<0)
                        {
                                perror("Error al obtener la dirección del segmento");
                                exit(EXIT_FAILURE);
                        }
                        else
                        {
				//Inicializamos las cuentas a 1000
				for (i=0;i<ncuentas;i++)
				{
					bufescritura[i]=1000;
				}
				//Mostramos el estado de las cuentas
				for (i=0;i<ncuentas;i++)
				{
					printf("El saldo de la cuenta %d es de %d \n",i+1,bufescritura[i]);
					dinerobanco=dinerobanco+bufescritura[i];
				}
				//Mostramos la cantidad total del dinero en el banco
				printf("El banco tiene %d \n",dinerobanco);
				printf("Voy a dormir 5 seg \n");
				//Creamos un semaforo
				semid=semget(llave,1,IPC_CREAT | 0600);
				//Lo abrimos
				semctl(semid,SEM_H,SETVAL,1);
				
				sleep(5);
				for(i=0;i<hijos;i++)
				{
					hijo=fork();
					switch (hijo)
					{
						case -1: {
							perror("Error al crear un hijo");
							exit(EXIT_FAILURE);
						}
						case 0: {
							int ntransferencias=100000;
							srand(getpid());
							while(ntransferencias>0)
							{
							int origen=0;
							int destino=0;
								while((origen==destino) || (bufescritura[origen]==0))
								{
								origen=rand()%ncuentas;
								destino=rand()%ncuentas;
								}
								ntransferencias--;							
							SEM_WAIT(SEM_H,semid,oper,0);
							system("clear");
							dinerobanco=0;
						        for (i=0;i<ncuentas;i++)
						        {
						                printf("El saldo de la cuenta %d es de %d \n",i+1,bufescritura[i]);
						                dinerobanco=dinerobanco+bufescritura[i];
						        }
							printf("El banco tiene %d \n",dinerobanco);
							//printf("Voy a transferir 1 unidad de %d a %d \n",origen,destino);
							bufescritura[origen]--;
							bufescritura[destino]++;
							SEM_POST(SEM_H,semid,oper,0);
							}
							exit(EXIT_SUCCESS);
						}
					}
				}
				wait(NULL);
				semctl(semid,0, IPC_RMID,0);
				sleep(2);
				//Mostramos el estado de las cuentas
				dinerobanco=0;
                                for (i=0;i<ncuentas;i++)
                                {
                                        printf("El saldo de la cuenta %d es de %d \n",i+1,bufescritura[i]);
                                        dinerobanco=dinerobanco+bufescritura[i];
                                }
                                //Mostramos la cantidad total del dinero en el banco
                                printf("El banco tiene %d \n",dinerobanco);

				printf("Fin proceso Padre\n");
                                if (shmdt(bufescritura)<0)
                                {
                                        perror("Error al desacoplar el segmento de memoria");
                                        exit(EXIT_FAILURE);
                                }
                                exit(EXIT_SUCCESS);
                        }
                }
        }
}
